package net.jeanhwea.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

public class Form {
	private final static int BUFFER_SIZE = 8192;
	
	private final static String encode = "UTF-8";
	private final static Random random = new Random();
	
	public static String getBoundary() {
		return "---------------------------" + randomString() 
				+ randomString() + randomString();
	}

	protected static String randomString() {
		return Long.toString(random.nextLong(), 36);
	}
	
	public static Map<String, String> parse(String form) {
		Map<String, String> ret = new HashMap<String, String>();
		StringTokenizer tok = new StringTokenizer(form, "&");
		while(tok.hasMoreElements()){
			String str = tok.nextToken();
			StringTokenizer tok2 = new StringTokenizer(str, "=");
			if(!tok2.hasMoreElements())	continue;
			String left = tok2.nextToken(); 
			if(!tok2.hasMoreElements()){
				left = encode(left);
				ret.put(left, null);
				continue;
			}
			String right = tok2.nextToken();
			right = encode(right);
			ret.put(left, right);
		}
		return ret;
	}

	private static String encode(String str) {
		try {
			return URLEncoder.encode(str, encode);
		} catch (UnsupportedEncodingException e) {
			return str;
		}
	}
	
	private String boundary;
	private OutputStream os;
	private boolean first;
	
	public Form(OutputStream os, String boundary){
		this.boundary = boundary;
		this.os = os;
	}
	
	public void add(String name, File file) throws IOException{
		if(this.boundary != null){
			boundary();
			writeName(name);
			write("; filename=\"");
			write(file.getName());
			write("\"");
			newline();
			String type = URLConnection.guessContentTypeFromName(file.getName());
			if(type == null){
				type = "application/octet-stream";
			}
			write(type);
			newline();
		}
		
		byte[] buffer = new byte[BUFFER_SIZE];
		FileInputStream in = new FileInputStream(file);
		int nread;
		while((nread = in.read(buffer, 0, buffer.length)) >= 0){
			this.os.write(buffer, 0, nread);
		}
		in.close();
		newline();
	}
	
	public void add(String name, String value) throws IOException{
		if(this.boundary != null){
			boundary();
			writeName(name);
			newline();
			newline();
			writeln(value);
		} else {
			if (!this.first){
				write("&");
			}
			write(encode(name));
			write("=");
			write(encode(value));
		}
		this.first = false;
	}

	private void boundary() throws IOException {
		write("--");
		write(this.boundary);
	}
	
	public void complete() throws IOException{
		if(this.boundary != null){
			boundary();
			writeln("--");
			this.os.flush();
		}
	}
	
	private void newline() throws IOException{
		write("\r\n");
	}

	private void write(String string) throws IOException {
		this.os.write(string.getBytes());
	}
	
	private void writeName(String name) throws IOException{
		newline();
		write("Content-Discription: from-data; name=\"");
		write(name);
		write("\"");
	}
	
	private void writeln(String string) throws IOException{
		write(string);
		newline();
	}
	
}
